/** @file    FilePath.cpp
 *  @author  Alessandro Polo
 *  @version $Id: FilePath.cpp 2621 2010-06-04 12:23:48Z alex $
 *  @brief
 * File containing methods for the wosh::FilePath class.
 * The header for this class can be found in FilePath.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <core/FilePath.h>

 #include <core/ObjectAllocator.h>
 #include <core/FileSystem.h>

 #include <cstdio> //for remove( ) and rename( )

#ifdef _OS_POSIX
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <dirent.h>
#else
 #ifdef _OS_WINCE
  #include <winbase.h>
  #include <core/wince/stringutils.h>
 #else
  #include <core/win32/utils.h>
  #include <core/win32/dirent.h>
 #endif
#endif


using namespace std;
namespace wosh {

 WOSH_REGISTER(wosh::FilePath, "wosh::ResourcePath", 1.01, _static_FilePath)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

FilePath::FilePath()
	: ResourcePath() {
	this->protocol = _ResourcePath_ProtocolFile;
 }

FilePath::FilePath( const string& wosh_path )
	: ResourcePath() {
	this->protocol = _ResourcePath_ProtocolFile;
	set(wosh_path);
 }

FilePath::FilePath( const FilePath& m )
	: ResourcePath(m) {
	 this->pathLocal = m.pathLocal;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool FilePath::set( const string& wosh_path ) {
	if ( !ResourcePath::set( wosh_path ) ) return false;
	// path formatting:
	// - NOT resolved (example. $MUSIC/mysong.mp3)
	// - platform independent, using POSIX separator (/)
	// - NO final slash (even for directories)
	Utilities::trimSpaces(this->path);
	if ( this->path.size() == 0 )
		return false;
	this->pathLocal = this->path;
	FilePath::resolve( this->pathLocal );
	FilePath::toLocalPath( this->pathLocal );
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool FilePath::fromLocalPath( const string& local_path ) {
	if ( local_path.empty() ) return false;
	this->pathLocal = local_path;
	if ( this->pathLocal.find_last_of(_FileSystem_PathSeparatorLocal) == this->pathLocal.size()-1 )
		this->pathLocal = this->pathLocal.substr(0, this->pathLocal.size()-2);

	return FileSystem::resolveLocalPath(this->pathLocal, this->path);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool FilePath::exists( const string& filepath ) {
	return ( FilePath::isFile(filepath) || FilePath::isFolder(filepath) );
 }

bool FilePath::isFolder( const string& local_path )
 {
#ifdef _OS_WINCE // WINCE doesn't support..
	std::Wstring dirname = ToWString(local_path);
	return ( GetFileAttributes(dirname.c_str()) == FILE_ATTRIBUTE_DIRECTORY );
#else
	DIR *dp = opendir(local_path.c_str());
	if ( dp == NULL )
		return false;
	closedir(dp);
	return true;
#endif
 }

bool FilePath::isFile( const string& local_path ) {
	FILE *istream = NULL;
	if ( (istream = fopen( local_path.c_str(), "r" ) ) == NULL )
		return false;
	fclose ( istream );
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

string FilePath::getFolder( const string& file_path ) {
	// final slash is always cropped, so we remove last chunk
	size_t pos = file_path.find_last_of(_FileSystem_PathSeparator);
	if ( pos == string::npos ) {
		pos = file_path.find_last_of(_FileSystem_PathSeparatorLocal);
		if ( pos == string::npos )
			return file_path;
		return file_path.substr(0, pos);
	 }
	return file_path.substr(0, pos);
 }

string FilePath::getName( const string& file_path ) {
	// final slash is always cropped, so we keep last chunk
	size_t pos = file_path.find_last_of(_FileSystem_PathSeparator);
	if ( pos == string::npos )
		return "";
	return file_path.substr( pos+1 );
 }

string FilePath::getExtension( const string& file_path, short change_case ) {
	std::size_t found = file_path.find_last_of(".");
	if ( found == std::string::npos || found == file_path.size() )
		return "";
	if ( change_case == 1 )
		return Utilities::toUpper( file_path.substr( found+1 ) );
	else if ( change_case == 2 )
		return Utilities::toLower( file_path.substr( found+1 ) );
	return file_path.substr( found+1 );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

string FilePath::toPathLocal( bool resolve_path ) const {
	if ( this->path.empty() ) return "";
	vector<string> items;
	string temp = this->path;
	if ( resolve_path )
		resolve( temp );
	Utilities::splitString(temp, _FileSystem_PathSeparator, items, true, true);
	return Utilities::joinVector(items, _FileSystem_PathSeparatorLocal);
 }

void FilePath::toLocalPath( const string& wosh_path, string& local_path ) {
	if ( wosh_path.empty() ) return;
	vector<string> items;
	Utilities::splitString(wosh_path, _FileSystem_PathSeparator, items, true, true);
	local_path = Utilities::joinVector(items, _FileSystem_PathSeparatorLocal);
 }

void FilePath::toLocalPath( string& path ) {
	if ( path.empty() ) return;
	vector<string> items;
	Utilities::splitString(path, _FileSystem_PathSeparator, items, true, true);
	path = Utilities::joinVector(items, _FileSystem_PathSeparatorLocal);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void FilePath::fixPath( const string& generic_path, string& wosh_path ) {
	vector<string> items;
	Utilities::splitString(generic_path, _FileSystem_PathSeparatorLocal, items, true, true); // so we keep initial/final slash
	wosh_path = Utilities::joinVector(items, _FileSystem_PathSeparator);
	if ( wosh_path.size() == 0 )
		return;
	if ( wosh_path.substr(wosh_path.size()-1) == _FileSystem_PathSeparator )
		wosh_path = wosh_path.substr(0,wosh_path.size()-1);
 }

void FilePath::fixPath( string& path ) {
	vector<string> items;
	int found = Utilities::splitString(path, _FileSystem_PathSeparatorLocal, items, true, true); // so we keep initial/final/double slash
	if ( found < 1 ) {
		path = "";
		return;
	 }

	vector<string>::iterator it = items.begin();
	while(true) {
		++it;// so we keep initial slash
		if ( it == items.end() )
			break;
		if ( (*it).empty() ) {
			if ( it+1 == items.end() ) break;// so we keep final slash
			items.erase(it);
			++it;
			continue;
		 }
	 }
	path = Utilities::joinVector(items, _FileSystem_PathSeparator);
	if ( path.size() == 0 )
		return;
	if ( path.substr(path.size()-1) == _FileSystem_PathSeparator )
		path = path.substr(0,path.size()-1);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void FilePath::resolve( string& path ) {
	FilePath::fixPath(path);
	vector<string> items;
	Utilities::splitString(path, _FileSystem_PathSeparator, items, true, true); // so we keep initial/final slash
	std::vector<std::string>::iterator it;
	bool found=true; int i=0; const int MAX=16000;
	do {
		found = false;
		for ( it=items.begin(); it!=items.end(); ++it ) {
			if ( !FileSystem::resolveAlias(*it) )
				continue;
			found = true;
			vector<string> sub_items;
			Utilities::splitString(*it, _FileSystem_PathSeparator, sub_items, true, true);
			if ( sub_items.empty() )
				continue;
			it = items.erase(it);
			items.insert(it, sub_items.begin(), sub_items.end() );
			break;
		 }
	 }
	while(found && i++ < MAX);
	path = Utilities::joinVector(items, _FileSystem_PathSeparator);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

string FilePath::combine( const string& path_0, const string& path_1 ) {
	if ( path_1.empty() ) return path_0;
	if ( path_0.empty() ) return path_1;
	string res = "";

	if ( path_0.find_last_of(_FileSystem_PathSeparator) == path_0.size()-1 ||
		 path_0.find_last_of(_FileSystem_PathSeparatorLocal) == path_0.size()-1 )
		res += path_0.substr(0, path_0.size()-1);
	else
		res += path_0;

	res += _FileSystem_PathSeparator;

	if ( path_1.find(_FileSystem_PathSeparator) == 0 || path_1.find(_FileSystem_PathSeparatorLocal) == 0 )
		res += path_1.substr(1);
	else
		res += path_1;

	return res;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
